package com.hczt.fire.framework.swagger.configuration;


import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.hczt.fire.framework.swagger.properties.SwaggerProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
 * @author marvin.ma
 * @create 2017-09-05 11:14
 * @desc swagger自动配置
 **/
@Slf4j
@EnableSwagger2
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerConfiguration implements BeanFactoryAware {

  private BeanFactory beanFactory;

  @Bean
  @ConditionalOnMissingBean
  public SwaggerProperties swaggerProperties() {
    return new SwaggerProperties();
  }

  @Bean
  @ConditionalOnMissingBean
  public List<Docket> createRestApi(SwaggerProperties swaggerProperties) {
    ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;

    // 没有分组
    if(swaggerProperties.getDocket().size() == 0) {
      ApiInfo apiInfo = new ApiInfoBuilder()
              .title(swaggerProperties.getTitle())
              .description(swaggerProperties.getDescription())
              .version(swaggerProperties.getVersion())
              .license(swaggerProperties.getLicense())
              .licenseUrl(swaggerProperties.getLicenseUrl())
              .contact(new Contact(swaggerProperties.getContact().getName(),
                      swaggerProperties.getContact().getUrl(),
                      swaggerProperties.getContact().getEmail()))
              .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
              .build();

      // base-path处理
      // 当没有配置任何path的时候，解析/**
      if(swaggerProperties.getBasePath().isEmpty()) {
        swaggerProperties.getBasePath().add("/**");
      }
      List<Predicate<String>> basePath = new ArrayList();
      for(String path : swaggerProperties.getBasePath()) {
        basePath.add(PathSelectors.ant(path));
      }

      // exclude-path处理
      List<Predicate<String>> excludePath = new ArrayList();
      for(String path : swaggerProperties.getExcludePath()) {
        excludePath.add(PathSelectors.ant(path));
      }

      Docket docket = new Docket(DocumentationType.SWAGGER_2)
              .host(swaggerProperties.getHost())
              .apiInfo(apiInfo)
              .select()
              .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
              .paths(
                      Predicates.and(
                              Predicates.not(Predicates.or(excludePath)),
                              Predicates.or(basePath)
                      )
              )
              .build();

      configurableBeanFactory.registerSingleton("defaultDocket", docket);
      return null;
    }

    // 分组创建
    List<Docket> docketList = new LinkedList<>();
    for(String groupName : swaggerProperties.getDocket().keySet()) {
      SwaggerProperties.DocketInfo docketInfo = swaggerProperties.getDocket().get(groupName);

      ApiInfo apiInfo = new ApiInfoBuilder()
              .title(docketInfo.getTitle().isEmpty() ? swaggerProperties.getTitle() : docketInfo.getTitle())
              .description(docketInfo.getDescription().isEmpty() ? swaggerProperties.getDescription() : docketInfo.getDescription())
              .version(docketInfo.getVersion().isEmpty() ? swaggerProperties.getVersion() : docketInfo.getVersion())
              .license(docketInfo.getLicense().isEmpty() ? swaggerProperties.getLicense() : docketInfo.getLicense())
              .licenseUrl(docketInfo.getLicenseUrl().isEmpty() ? swaggerProperties.getLicenseUrl() : docketInfo.getLicenseUrl())
              .contact(
                      new Contact(
                              docketInfo.getContact().getName().isEmpty() ? swaggerProperties.getContact().getName() : docketInfo.getContact().getName(),
                              docketInfo.getContact().getUrl().isEmpty() ? swaggerProperties.getContact().getUrl() : docketInfo.getContact().getUrl(),
                              docketInfo.getContact().getEmail().isEmpty() ? swaggerProperties.getContact().getEmail() : docketInfo.getContact().getEmail()
                      )
              )
              .termsOfServiceUrl(docketInfo.getTermsOfServiceUrl().isEmpty() ? swaggerProperties.getTermsOfServiceUrl() : docketInfo.getTermsOfServiceUrl())
              .build();

      // base-path处理
      // 当没有配置任何path的时候，解析/**
      if(docketInfo.getBasePath().isEmpty()) {
        docketInfo.getBasePath().add("/**");
      }
      List<Predicate<String>> basePath = new ArrayList();
      for(String path : docketInfo.getBasePath()) {
        basePath.add(PathSelectors.ant(path));
      }

      // exclude-path处理
      List<Predicate<String>> excludePath = new ArrayList();
      for(String path : docketInfo.getExcludePath()) {
        excludePath.add(PathSelectors.ant(path));
      }

      Docket docket = new Docket(DocumentationType.SWAGGER_2)
              .host(swaggerProperties.getHost())
              .apiInfo(apiInfo)
              .groupName(groupName)
              .select()
              .apis(RequestHandlerSelectors.basePackage(docketInfo.getBasePackage()))
              .paths(
                      Predicates.and(
                              Predicates.not(Predicates.or(excludePath)),
                              Predicates.or(basePath)
                      )
              )
              .build();

      configurableBeanFactory.registerSingleton(groupName, docket);
      docketList.add(docket);
    }
    return docketList;
  }

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
  }

}
